﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using DomainDriver.CommonUtilities.Reflectors;
using DomainDriver.DomainModeling.Constraints;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.DomainModels.ConstructionResources;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Events;
using DomainDriver.DomainModeling.Repositories;
using DomainDriver.DomainModeling.StorageManagers;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using DomainDriver.DomainModeling.Example;
using DomainDriver.DomainModeling.Example.DomainObjects;
using DummyDomainObjectRow = DomainDriver.DomainModeling.Example.ExampleModelDataSet.DummyDomainObjectsRow;

namespace DomainDriver.DomainModeling.Example
{
    public class ExampleDomainModel : DomainModel, IBatchPersistableDomainModel<ExampleModelDataSet>
    {
        public const string DBConnectionName = "DomainDriver.DomainModeling.Example.DataSources.ExampleModelDatabaseContext";

        #region Static Members

        private static readonly ExampleModelDataSet m_SchemaHelper = new ExampleModelDataSet();
        public static ExampleModelDataSet SchemaHelper
        {
            get { return m_SchemaHelper; }
        }

        #endregion

        #region Constructors

        public ExampleDomainModel(DataSourcePersistenceType persistenceType, bool buildModelWithConstraints)
            : this(persistenceType, InvalidCallAction.Ignore, buildModelWithConstraints)
        { }

        public ExampleDomainModel(DataSourcePersistenceType persistenceType, InvalidCallAction invalidCallAction, bool buildModelWithConstraints)
            : base(persistenceType, invalidCallAction, buildModelWithConstraints)
        {
            RegisterDataSource<ExampleModelDataSet>(DataSourcePersistenceType.File, RepositoryConstructionMethods.CreateDataSetBackedRepository);
            RegisterDataSource<ExampleModelDatabaseContext>(DataSourcePersistenceType.Database, RepositoryConstructionMethods.CreateEntityFrameworkBackedRepository);

            RegisterDomainObjectRepository<Guid, DummyDomainObject>();
            RegisterDomainObjectRepository<EnumerationValue, int, EnumerationDomainObject>();
            RegisterDomainObjectRepository<Guid, SingletonDomainObject>();

            CreateConstraints += ConstraintCreationHandler;

            Initialize();
        }

        public void ConstraintCreationHandler(object sender, EventArgs args)
        {
            string ddoIndexPropertyName = ClassReflector.GetPropertyName<DummyDomainObject, int>((DummyDomainObject dObj) => dObj.Index);
            List<string> ddoUniqueOnPropertyNames = new List<string>();
            ddoUniqueOnPropertyNames.Add(ClassReflector.GetPropertyName<DummyDomainObject, Guid>((DummyDomainObject dObj) => (Guid)dObj.Parent));
            IndexConstraint<Guid, DummyDomainObject> ddoIndexConstraint = new IndexConstraint<Guid, DummyDomainObject>(this, ddoIndexPropertyName, ddoUniqueOnPropertyNames);
            AddModelConstraint(ddoIndexConstraint);

            string edoIndexPropertyName = ClassReflector.GetPropertyName<EnumerationDomainObject, int>((EnumerationDomainObject dObj) => dObj.Index);
            List<string> edoUniqueOnPropertyNames = new List<string>();
            IndexConstraint<EnumerationValue, EnumerationDomainObject> edoIndexConstraint = new IndexConstraint<EnumerationValue, EnumerationDomainObject>(this, edoIndexPropertyName, edoUniqueOnPropertyNames);
            AddModelConstraint(edoIndexConstraint);

            ListRelationConstraint<Guid, Guid, DummyDomainObject, DummyDomainObject> childConstraint = new ListRelationConstraint<Guid, Guid, DummyDomainObject, DummyDomainObject>(this, (DummyDomainObject dObj) => dObj.Children);
            AddModelConstraint(childConstraint);

            ParentChildConstraint<Guid, Guid, DummyDomainObject, DummyDomainObject> parentConstraint = new ParentChildConstraint<Guid, Guid, DummyDomainObject, DummyDomainObject>(this, (DummyDomainObject dObj) => (Guid)dObj.Parent);
            AddModelConstraint(parentConstraint);

            ParentChildConstraint<EnumerationValue, Guid, EnumerationDomainObject, SingletonDomainObject> singletonParentConstraint = new ParentChildConstraint<EnumerationValue, Guid, EnumerationDomainObject, SingletonDomainObject>(this, (SingletonDomainObject dObj) => (EnumerationValue)dObj.SelectedEnumerationDomainObjectId);
            AddModelConstraint(singletonParentConstraint);

            SingletonConstraint<Guid, SingletonDomainObject> singletonConstraint = new SingletonConstraint<Guid, SingletonDomainObject>(this, SingletonDomainObject.CreateSingleton);
            AddModelConstraint(singletonConstraint);

            // LATER: Put this last to test that this causes issues when selected id is null in singleton
            FixedValuesConstraint<EnumerationValue, EnumerationDomainObject> enumerationFixedValuesConstraint = new FixedValuesConstraint<EnumerationValue, EnumerationDomainObject>(this, EnumerationDomainObject.LoadEnumerationsValues);
            AddModelConstraint(enumerationFixedValuesConstraint);
        }

        #endregion

        #region Persistence Code

        public void LoadFromDataSource(ExampleModelDataSet dataSource)
        {
            object dataSourceAsObject = dataSource;
            LoadFromDataSource(dataSourceAsObject);
        }

        public void PersistToDataSource(ExampleModelDataSet dataSource)
        {
            object dataSourceAsObject = dataSource;
            PersistToDataSource(dataSourceAsObject);
        }

        #endregion
    }
}